package com.swy.service.impl;

import com.swy.model.FileDetectionResult;
import com.swy.model.SensitiveCheckResult;
import com.swy.service.SensitiveDetectorService;
import com.swy.service.SensitiveWordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.sl.extractor.SlideShowExtractor;
import org.apache.poi.xslf.usermodel.XSLFShape;
import org.apache.poi.xslf.usermodel.XSLFTextParagraph;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.HSLFShape;
import org.apache.poi.hslf.usermodel.HSLFTextParagraph;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SensitiveDetectorServiceImpl implements SensitiveDetectorService {

    private final SensitiveWordService sensitiveWordService;
    private static final List<String> SUPPORTED_EXTENSIONS = Arrays.asList(
            ".txt", ".docx", ".xlsx", ".xls", ".pptx", ".ppt", ".pdf"
    );

    @Override
    public List<CompletableFuture<SensitiveCheckResult>> detectFiles(MultipartFile[] files, String mode) {
        return Arrays.stream(files)
                .map(file -> detectFile(file, mode))
                .collect(Collectors.toList());
    }

    private CompletableFuture<SensitiveCheckResult> detectFile(MultipartFile file, String mode) {
        return CompletableFuture.supplyAsync(() -> {
            FileDetectionResult detectionResult = new FileDetectionResult();
            detectionResult.setFileName(file.getOriginalFilename());
            
            try {
                String content = extractContent(file);
                detectionResult.setFileContent(content);
                detectionResult.setFileType(getFileExtension(file.getOriginalFilename()));
                detectionResult.setEncoding(detectEncoding(file));
                
                if ("fuzzy".equalsIgnoreCase(mode)) {
                    java.util.List<com.swy.service.impl.SensitiveWordServiceImpl.FuzzyMatchResult> fuzzyResults = sensitiveWordService.detectSensitiveWordsFuzzy(content);
                    detectionResult.setHasSensitiveWords(!fuzzyResults.isEmpty());
                    List<SensitiveCheckResult.SensitiveWordContext> contexts = new ArrayList<>();
                    for (com.swy.service.impl.SensitiveWordServiceImpl.FuzzyMatchResult match : fuzzyResults) {
                        String matchedSub = match.getMatchedSub();
                        String motherWord = match.getSensitiveWord();
                        int index = content.indexOf(matchedSub);
                        while (index != -1) {
                            SensitiveCheckResult.SensitiveWordContext context = new SensitiveCheckResult.SensitiveWordContext();
                            context.setWord(motherWord);
                            context.setPosition(index);
                            int contextStart = Math.max(0, index - 30);
                            int contextEnd = Math.min(content.length(), index + matchedSub.length() + 30);
                            String extractedContext = content.substring(contextStart, contextEnd);
                            context.setContext(extractedContext);
                            context.setWordStartPos(extractedContext.indexOf(matchedSub));
                            context.setWordEndPos(extractedContext.indexOf(matchedSub) + matchedSub.length());
                            contexts.add(context);
                            index = content.indexOf(matchedSub, index + 1);
                        }
                    }
                    SensitiveCheckResult result = new SensitiveCheckResult();
                    result.setFileName(detectionResult.getFileName());
                    result.setHasSensitiveWords(!fuzzyResults.isEmpty());
                    result.setSensitiveWordContexts(contexts);
                    return result;
                } else {
                    List<String> sensitiveWords = sensitiveWordService.detectSensitiveWords(content);
                    detectionResult.setHasSensitiveWords(!sensitiveWords.isEmpty());
                    detectionResult.setSensitiveWords(sensitiveWords);
                    SensitiveCheckResult finalResult = convertToCheckResult(detectionResult);
                    return finalResult;
                }
            } catch (Exception e) {
                String errorMsg = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
                String exceptionClass = e.getClass().getSimpleName().toLowerCase();
                if (errorMsg.contains("password") || errorMsg.contains("encrypt") || exceptionClass.contains("encrypt")) {
                    detectionResult.setErrorMessage("文件被加密或需要密码，请解除加密后再上传。");
                } else if (errorMsg.contains("protect") || errorMsg.contains("只读") || errorMsg.contains("readonly")) {
                    detectionResult.setErrorMessage("文件处于受保护或只读状态，请启用编辑并保存后再上传。");
                } else if (errorMsg.contains("format") || errorMsg.contains("unsupported") || errorMsg.contains("损坏") || errorMsg.contains("corrupt")) {
                    detectionResult.setErrorMessage("文件格式不支持、已损坏或被加密。");
                } else {
                    detectionResult.setErrorMessage("文件检测失败: " + e.getMessage());
                }
                SensitiveCheckResult errorResult = convertToCheckResult(detectionResult);
                return errorResult;
            }
        });
    }

    private SensitiveCheckResult convertToCheckResult(FileDetectionResult detectionResult) {
        SensitiveCheckResult result = new SensitiveCheckResult();
        result.setFileName(detectionResult.getFileName());
        result.setHasSensitiveWords(detectionResult.isHasSensitiveWords());
        result.setErrorMessage(detectionResult.getErrorMessage());
        
        if (detectionResult.isHasSensitiveWords() && detectionResult.getSensitiveWords() != null) {
            List<SensitiveCheckResult.SensitiveWordContext> contexts = new ArrayList<>();
            String content = detectionResult.getFileContent();
            for (String word : detectionResult.getSensitiveWords()) {
                int index = content.indexOf(word);
                while (index != -1) {
                    SensitiveCheckResult.SensitiveWordContext context = new SensitiveCheckResult.SensitiveWordContext();
                    context.setWord(word);
                    context.setPosition(index);
                    int contextStart = Math.max(0, index - 30);
                    int contextEnd = Math.min(content.length(), index + word.length() + 30);
                    String extractedContext = content.substring(contextStart, contextEnd);
                    context.setContext(extractedContext);
                    context.setWordStartPos(extractedContext.indexOf(word));
                    context.setWordEndPos(extractedContext.indexOf(word) + word.length());
                    contexts.add(context);
                    index = content.indexOf(word, index + 1);
                }
            }
            result.setSensitiveWordContexts(contexts);
        }
        
        return result;
    }

    private String extractContent(MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename().toLowerCase();
        
        if (fileName.endsWith(".txt")) {
            return extractTextContent(file);
        } else if (fileName.endsWith(".docx")) {
            return extractDocxContent(file);
        } else if (fileName.endsWith(".xlsx") || fileName.endsWith(".xls")) {
            return extractExcelContent(file);
        } else if (fileName.endsWith(".pptx") || fileName.endsWith(".ppt")) {
            return extractPptContent(file);
        } else if (fileName.endsWith(".pdf")) {
            return extractPdfContent(file);
        }
        
        throw new IOException("不支持的文件格式: " + fileName);
    }

    private String extractTextContent(MultipartFile file) throws IOException {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(file.getInputStream(), detectEncoding(file)))) {
            return reader.lines().collect(Collectors.joining("\n"));
        }
    }

    private String extractDocxContent(MultipartFile file) throws IOException {
        try (XWPFDocument document = new XWPFDocument(file.getInputStream())) {
            XWPFWordExtractor extractor = new XWPFWordExtractor(document);
            return extractor.getText();
        }
    }

    private String extractExcelContent(MultipartFile file) throws IOException {
        try (XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream())) {
            XSSFExcelExtractor extractor = new XSSFExcelExtractor(workbook);
            return extractor.getText();
        }
    }

    private String extractPptContent(MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename().toLowerCase();

        if (fileName.endsWith(".pptx")) {
            try (XMLSlideShow slideshow = new XMLSlideShow(file.getInputStream())) {
                SlideShowExtractor<XSLFShape, XSLFTextParagraph> extractor = new SlideShowExtractor<>(slideshow);
                String extractedText = extractor.getText();
                return extractedText;
            } catch (Exception e) {
                throw new IOException("PPTX内容提取失败", e);
            }
        } else if (fileName.endsWith(".ppt")) {
            try (HSLFSlideShow slideshow = new HSLFSlideShow(file.getInputStream())) {
                SlideShowExtractor<HSLFShape, HSLFTextParagraph> extractor = new SlideShowExtractor<>(slideshow);
                String extractedText = extractor.getText();
                return extractedText;
            } catch (Exception e) {
                throw new IOException("PPT内容提取失败", e);
            }
        } else {
            throw new IOException("不支持的PPT文件格式: " + fileName);
        }
    }

    private String extractPdfContent(MultipartFile file) throws IOException {
        try (PDDocument document = PDDocument.load(file.getInputStream())) {
            PDFTextStripper stripper = new PDFTextStripper();
            return stripper.getText(document);
        }
    }

    private String detectEncoding(MultipartFile file) throws IOException {
        byte[] bytes = file.getBytes();
        if (isUtf8(bytes)) {
            return StandardCharsets.UTF_8.name();
        } else if (isGBK(bytes)) {
            return "GBK";
        }
        return StandardCharsets.ISO_8859_1.name();
    }

    private boolean isUtf8(byte[] bytes) {
        try {
            String str = new String(bytes, StandardCharsets.UTF_8);
            byte[] reEncoded = str.getBytes(StandardCharsets.UTF_8);
            return Arrays.equals(bytes, reEncoded);
        } catch (Exception e) {
            return false;
        }
    }

    private boolean isGBK(byte[] bytes) {
        try {
            String str = new String(bytes, "GBK");
            byte[] reEncoded = str.getBytes("GBK");
            return Arrays.equals(bytes, reEncoded);
        } catch (Exception e) {
            return false;
        }
    }

    private String getFileExtension(String fileName) {
        int lastDotIndex = fileName.lastIndexOf('.');
        return lastDotIndex > 0 ? fileName.substring(lastDotIndex).toLowerCase() : "";
    }

    @Override
    public void cleanUploadCache() {
        // TODO: 实现缓存清理逻辑
    }

    @Override
    public void updateCleanPeriod(int period) {
        // TODO: 实现清理周期更新逻辑
    }
} 